/**
 * request 网络请求工具
 * 更详细的 api 文档: https://github.com/umijs/umi-request
 */
import { RequestOptionsInit, ResponseError, extend } from 'umi-request';
import { notification, message } from 'antd';
import { history } from 'umi';
import { stringify } from 'qs';
import { guid } from './commons';
import { deleteButtons, deleteRoutes, getToken } from './authority';

// 与后端约定的响应数据格式
type ResponseStructure = {
  success: boolean;
  result?: any;
  errorCode?: string;
  errorMsg?: string;
}

/**
 * 自定义错误
 */
type OaBusiError = Omit<ResponseStructure, 'result'>

/**
 * http状态码映射信息
 */
type HttpCodeMessageType = Record<number, string>;

/**
 * 非登录状态错误映射信息
 */
type NoLoginMessageType = Record<string, string>;

/**
 * http状态码映射信息
 */
const httpCodeMessage: HttpCodeMessageType = {
  200: '服务器成功返回请求的数据。',
  201: '新建或修改数据成功。',
  202: '一个请求已经进入后台排队（异步任务）。',
  204: '删除数据成功。',
  400: '发出的请求有错误，服务器无法处理。',
  401: '用户没有权限（令牌、用户名、密码错误）。',
  403: '用户得到授权，但是访问是被禁止的。',
  404: '发出的请求针对的是不存在的记录，服务器没有进行操作。',
  406: '请求的格式不可得。',
  410: '请求的资源被永久删除，且不会再得到的。',
  422: '当创建一个对象时，发生一个验证错误。',
  500: '服务器发生错误，请检查服务器。',
  502: '网关错误。',
  503: '服务不可用，服务器暂时过载或维护。',
  504: '网关超时。',
}

const noLoginMessage: NoLoginMessageType = {
  'E0054': '请先登录，在进行相关操作',
  'E0055': 'Token验证失败，请重新登录',
  'E0056': 'Token已失效，请重新登录'
}

// const codeMessage = {
//   200: '服务器成功返回请求的数据。',
//   201: '新建或修改数据成功。',
//   202: '一个请求已经进入后台排队（异步任务）。',
//   204: '删除数据成功。',
//   400: '发出的请求有错误，服务器没有进行新建或修改数据的操作。',
//   401: '用户没有权限（令牌、用户名、密码错误）。',
//   403: '用户得到授权，但是访问是被禁止的。',
//   404: '发出的请求针对的是不存在的记录，服务器没有进行操作。',
//   406: '请求的格式不可得。',
//   410: '请求的资源被永久删除，且不会再得到的。',
//   422: '当创建一个对象时，发生一个验证错误。',
//   500: '服务器发生错误，请检查服务器。',
//   502: '网关错误。',
//   503: '服务不可用，服务器暂时过载或维护。',
//   504: '网关超时。',
// };

// const noLoginMessage = {
//   'E0054': '请先登录，在进行相关操作',
//   'E0055': 'Token验证失败，请重新登录',
//   'E0056': 'Token已失效，请重新登录'
// }

/**
 * 异常处理程序
 */
const errorHandler = (error: ResponseError<ResponseStructure>): OaBusiError => {
  console.info(error);
  const result: OaBusiError = {
    success: false
  }
  const resp = error.response
  if (resp && error.name === 'httperror') {
    const errorText = httpCodeMessage[resp.status] || resp.statusText;
    const url = error.request.url;
    notification.error({
      message: '请求错误 ' + resp.status + `: ${url}`,
      description: errorText,
    });
    return result;
  }
  // 只有200的应答码才会进来
  const { data } = error;
  // console.info(data);
  const { errorCode } = data;
  if (errorCode && errorCode.startsWith('E')) {
    const { errorMsg } = data;
    // 错误码为未登录或者token失效等，跳转到登录页面
    const errorMsg1 = noLoginMessage[errorCode];
    if (errorMsg1) {
      // 多个请求同时发起时，判断是否有请求已提示失效
      const tokenInvalid = sessionStorage.getItem('token-invalid');
      if (tokenInvalid === '1') {
        return result;
      }
      sessionStorage.setItem('token-invalid', '1');
      // message.warning(msg);
      // console.info(window.location);
      // console.info(window.location.href);
      // console.info(window.location.pathname);
      // const pathName = window.location.pathname;
      // 登录失效了，先删菜单及按钮数据
      deleteRoutes();
      deleteButtons();
      // 先提示后跳转
      message.error(`${errorCode} ${errorMsg}`);
      if (window.location.pathname.includes('/workbench/task/')) {
        // 需求待办任务的跳转直接回到登录界面，但不要登录后跳回
        history.replace({
          pathname: '/user/login',
        });
      } else {
        history.replace({
          pathname: '/user/login',
          search: stringify({
            // 记录从那个页面跳转跳转到登录页面的，登录后在跳转回去
            redirect: window.location.href
          })
        });
      }
    } else {
      message.error(`${errorCode} ${errorMsg}`);
    }
    return result;
  }
  // 没有errorCode，表示非业务失败
  // 非业务失败错误码
  const errorCode2 = 'E9999';
  const errorMsg2 = '系统异常，请联系技术人员';
  const url = error.request.url;
  notification.error({
    message: `请求发生异常 ${url}`,
    description: `${errorCode2} ${errorMsg2}`,
  });
  return result;
};

/**
 * 配置request请求时的默认参数
 */
const request = extend({
  errorHandler, // 默认错误处理
  credentials: 'include', // 默认请求是否带上cookie
  parseResponse: true, // 是否对 response 做处理简化
});

/**
 * 请求时，报文头添加token和随机数，方便前后端查询问题
 */
request.interceptors.request.use((url: string, options: RequestOptionsInit) => {
  // 获取存储在本地的token
  const token = getToken();
  if (token) {
    const rnd = guid();
    const { headers = {} } = options || {};
    const tokenHeaders = {
      guid: rnd,
      token,
      ...headers
    }
    // console.info(`token: ${token}`);
    return {
      url,
      options: {
        ...options,
        headers: {
          ...tokenHeaders
        }
      }
    }
  }
  return {
    url,
    options: {
      ...options
    }
  }
});

/**
 * 响应时，统一拦截
 */
request.interceptors.response.use(async (response) => {
  // console.info(response.url, response);
  // 与后端约定，所有业务请求均返回200
  if (response.status != 200) {
    // console.info('非200');
    // 非200即可认定为非业务返回
    // 将其交给 errorHandler 处理
    const data: ResponseStructure = {
      success: false
    }
    const error: Partial<ResponseError<ResponseStructure>> = {
      name: 'httperror',
      data,
      response,
    }
    return Promise.reject(error);
  }
  // console.info('200');
  // 一种类型断言的语法
  const resp = await response.clone().json() as unknown as ResponseStructure;
  // console.info(resp);
  // 通过success来判断是否请求成功，具体情况看个人项目
  if (resp && resp.success) {
    sessionStorage.removeItem('token-invalid');
    return response;
  }
  const error: Partial<ResponseError<ResponseStructure>> = {
    name: 'bizerror',
    data: resp,
    response,
  }
  // 注意：需要reject出去才会在请求不成功或返回错误的code时调用errorHandler
  // 认定网络超时
  return Promise.reject(error);
});

export default request;
